package com.ts.filter;

import com.alibaba.fastjson.JSON;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.ts.common.core.utils.JWTUtils;
import com.ts.common.core.utils.Result;
import com.ts.properties.IgnorePathProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.ObjectUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;

@Component
@Slf4j
public class TokenFilter implements GlobalFilter {
    @Autowired
    private IgnorePathProperties ignorePathProperties;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpRequest request = exchange.getRequest();

        //判断当前接口需不需要校验
        String url = request.getURI().getPath();
        boolean isIgnore = isIgnorePath(url);
        //如果检测到是不需要校验的就放行
        if (isIgnore){
            return chain.filter(exchange);
        }

        //获取token
        HttpHeaders headers = request.getHeaders();
        String token = headers.getFirst("token");

        //判断token是否为空
        Result result = new Result();
        if (ObjectUtils.isEmpty(token)){
            return errorInfo(exchange,result.error(420,"请先登录"));
        }

        //判断token的合法性
        DecodedJWT decodedJWT = null;
        try {
            decodedJWT = JWTUtils.checkToken(token);
        }catch (JWTVerificationException e) {
            return errorInfo(exchange,result.error(420,"请重新登录"));
        }

        //解析出token中的用户信息
        String loginUser = decodedJWT.getClaim("loginUser").asString();

        //往下传递
//        request.mutate().header("userInfo",logonUser);
        ServerHttpRequest newRequest = mutateHeader(loginUser, headers, request); // 创建一个新的请求对象，创建一个心的请求头

        //放行
        return chain.filter(exchange.mutate().request(newRequest).build());//将新的请求对象往下传递

    }

    /**
     * 将旧的请求头分装到新的请求头中，只需要把用户的数据分装进入。
     * @param loginUser 用户信息
     * @param headers 旧的请求头
     * @param request
     * @return 将新的请求对象返回
     */
    private ServerHttpRequest mutateHeader(String loginUser, HttpHeaders headers, ServerHttpRequest request) {
        HttpHeaders newHeader = new HttpHeaders();
        newHeader.putAll(headers);
        //新的请求头删除token
        newHeader.remove("token");

        //将用户信息放入请求头中
        newHeader.add("userInfo",loginUser);

        // 创建一个新的请求对象，请求地址不能变
        ServerHttpRequest newRequest = request.mutate().uri(request.getURI()).build();

        // 、把新的请求对象中添加新的请求头
        newRequest = new ServerHttpRequestDecorator(newRequest) {
            @Override
            public HttpHeaders getHeaders() {
                return newHeader;
            }
        };
        return newRequest;
    }


    private Mono<Void> errorInfo(ServerWebExchange exchange, Result result) {
        return Mono.defer(() -> {
            // 把对象转成JSON
            byte[] bytes = bytes = JSON.toJSONBytes(result);
            // 获取响应对象
            ServerHttpResponse response = exchange.getResponse();
            // 响应响应类型
            response.getHeaders().add("Content-Type", MediaType.APPLICATION_JSON_VALUE);
            // 设置HTTP响应码
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            DataBuffer buffer = response.bufferFactory().wrap(bytes);
            return response.writeWith(Flux.just(buffer));
        });
    }

    private AntPathMatcher antPathMatcher = new AntPathMatcher();
    private boolean isIgnorePath(String url) {
        List<String> ignoreList = ignorePathProperties.getIgnorePath();

        for (String path : ignoreList) {
            if (antPathMatcher.match(path,url)){
                return true;
            };
        }
        return false;
    }
}
